/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "chip.h"
#include "internal/bus/bus_base/bus.h"
#include <chrono>
#include <functional>
#include <mc/log.h>
#include <thread>

using namespace dev;

chip_base::chip_base() {
}

chip_base::~chip_base() {
}

bool chip_base::init(mc::mutable_dict& csr_object) {
    try {
        // 从字典中提取各个属性值并设置到对象中
        if (csr_object.contains("Address")) {
            this->m_address = csr_object["Address"].as<uint32_t>();
        }
        if (csr_object.contains("OffsetWidth")) {
            this->m_offset_width = csr_object["OffsetWidth"].as<uint8_t>();
        }
        if (csr_object.contains("AddrWidth")) {
            this->m_address_width = csr_object["AddrWidth"].as<uint8_t>();
        }
        if (csr_object.contains("WriteTmout")) {
            this->m_write_timeout = csr_object["WriteTmout"].as<uint32_t>();
        }
        if (csr_object.contains("ReadTmout")) {
            this->m_read_timeout = csr_object["ReadTmout"].as<uint32_t>();
        }
        if (csr_object.contains("RwBlockSize")) {
            this->m_rw_block_size = csr_object["RwBlockSize"].as<uint8_t>();
        }
        if (csr_object.contains("WriteInterval")) {
            this->m_write_interval = csr_object["WriteInterval"].as<uint8_t>();
        }
        if (csr_object.contains("ReadInterval")) {
            this->m_read_interval = csr_object["ReadInterval"].as<uint8_t>();
        }
        if (csr_object.contains("WriteRetryTimes")) {
            this->m_write_retry_times = csr_object["WriteRetryTimes"].as<uint8_t>();
        }
        if (csr_object.contains("ReadRetryTimes")) {
            this->m_read_retry_times = csr_object["ReadRetryTimes"].as<uint8_t>();
        }

        return true;
    } catch (const std::exception& e) {
        elog("Chip configuration loading failed: ${error}", ("error", e.what()));
        return false;
    }
}

void chip_base::set_left_chip(std::shared_ptr<chip_base> left_chip) {
    this->m_left_chip = left_chip;
}

void chip_base::set_left_bus(std::shared_ptr<bus_base> left_bus) {
    this->m_left_bus = left_bus;
}

void chip_base::set_host_bus(std::shared_ptr<bus_base> host_bus) {
    this->m_host_bus = host_bus;
}

int32_t chip_base::chip_init() {
    return HAS_OK;
}

int32_t chip_base::chip_test(uint32_t* result) {
    return HAS_OK;
}

int32_t chip_base::chip_reset() {
    return HAS_OK;
}

int32_t chip_base::open_channel(std::shared_ptr<bus_base> bus) {
    return HAS_OK;
}

ACCESS_DATA_S chip_base::pack_access_data(uint32_t mask, uint32_t offset, uint32_t length,
                                          const std::vector<uint8_t>& in_buffer,
                                          std::vector<uint8_t>& out_buffer, uint8_t optype) {
    // 构建ACCESS_DATA_S结构体
    ACCESS_DATA_S input  = {};
    input.addr_width     = this->m_address_width;
    input.offset_width   = this->m_offset_width;
    input.mask           = mask;
    input.addr           = this->m_address;
    input.offset         = offset;
    input.length         = length;
    input.in_buffer      = in_buffer;
    input.out_buffer     = out_buffer;
    input.read_time_out  = this->m_read_timeout;
    input.write_time_out = this->m_write_timeout;
    input.optype         = optype;
    return input;
}

int32_t chip_base::output_to_input(ACCESS_DATA_S& output_data, ACCESS_DATA_S& input_data) {
    return HAS_OK;
}

int32_t chip_base::input(ACCESS_DATA_S& data) {
    // 本器件执行输入数据
    int32_t ret = HAS_ERR;

    // 检查前级芯片
    if (m_left_chip) {
        // 检查前级总线
        if (m_left_bus) {
            m_left_chip->open_channel(m_left_bus);
            // 数据由前级器件输出
            return m_left_chip->output(data);
        }
    }

    // 检查前级总线
    if (m_left_bus) {
        if (data.optype == CHIP_OP_TYPE_READ) {
            ret = m_left_bus->read(data);
        } else if (data.optype == CHIP_OP_TYPE_WRITE) {
            ret = m_left_bus->write(data);
        } else {
            ret = HAS_ERR;
        }
    } else {
        ret = HAS_ERR;
    }

    return ret;
}

int32_t chip_base::output(ACCESS_DATA_S& data) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        MC_THROW(mc::invalid_op_exception, "pre bus object not found");
    }

    ACCESS_DATA_S& input_data = data;
    output_to_input(data, input_data);
    return input(input_data);
}

int32_t chip_base::data_access(uint32_t mask, uint32_t offset, uint32_t length,
                                const std::vector<uint8_t>& in_buffer,
                                std::vector<uint8_t>& out_buffer, uint8_t optype) {
    ACCESS_DATA_S data   = pack_access_data(mask, offset, length, in_buffer, out_buffer, optype);
    int32_t      result = input(data);

    // 对于读取操作，将读取的数据移动回原始的 out_buffer
    if (optype == CHIP_OP_TYPE_READ && !data.out_buffer.empty()) {
        out_buffer = std::move(data.out_buffer);
    }

    return result;
}

int32_t chip_base::bit_read(uint32_t offset, uint8_t length, uint32_t mask,
                             std::vector<uint8_t>& in_buffer, std::vector<uint8_t>& out_buffer,
                             std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        return data_access(mask, offset, length, in_buffer, out_buffer, CHIP_OP_TYPE_READ);
    } catch (const std::exception& e) {
        error = std::string("bit_read error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_base::bit_write(uint32_t offset, uint8_t length, uint32_t mask,
                              const std::vector<uint8_t>& in_buffer,
                              std::vector<uint8_t>&       out_buffer,
                              std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        return data_access(mask, offset, length, in_buffer, out_buffer, CHIP_OP_TYPE_WRITE);
    } catch (const std::exception& e) {
        error = std::string("bit_write error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_base::block_read(uint32_t offset, uint32_t length, std::vector<uint8_t>& in_buffer,
                               std::vector<uint8_t>& out_buffer, std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        return data_access(0, offset, length, in_buffer, out_buffer, CHIP_OP_TYPE_READ);
    } catch (const std::exception& e) {
        error = std::string("block_read error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_base::block_write(uint32_t offset, const std::vector<uint8_t>& in_buffer,
                                std::vector<uint8_t>& out_buffer, std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        return data_access(0, offset, static_cast<uint32_t>(in_buffer.size()), in_buffer,
                           out_buffer, CHIP_OP_TYPE_WRITE);
    } catch (const std::exception& e) {
        error = std::string("block_write error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_base::block_write_read(uint32_t offset, const std::vector<uint8_t>& indata,
                                     uint32_t read_length, std::vector<uint8_t>& out_buffer,
                                     std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        return data_access(0, offset, read_length, indata, out_buffer, CHIP_OP_TYPE_READ);
    } catch (const std::exception& e) {
        error = std::string("block_write_read error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

int32_t chip_base::combo_block_write_read(uint32_t                    write_offset,
                                           const std::vector<uint8_t>& write_buffer,
                                           uint32_t read_offset, uint32_t read_length,
                                           std::vector<uint8_t>& in_buffer,
                                           std::vector<uint8_t>& out_buffer,
                                           std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        std::string write_error, read_error;
        auto write_ret = block_write(write_offset, write_buffer, in_buffer, write_error);
        if (write_ret != HAS_OK) {
            error = "combo_block_write_read write failed: " + write_error;
            return write_ret;
        }
        auto read_ret = block_read(read_offset, read_length, in_buffer, out_buffer, read_error);
        if (read_ret != HAS_OK) {
            error = "combo_block_write_read read failed: " + read_error;
            return read_ret;
        }
        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("combo_block_write_read error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

// 通用重试工具函数
template <typename Func>
int32_t retry_with_sleep(Func&& func, int max_retries = 3, int sleep_ms = 30) {
    int32_t ret = HAS_OK;
    for (int i = 0; i < max_retries; ++i) {
        ret = func();
        if (ret == HAS_OK) {
            break;
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
    }
    return ret;
}

int32_t chip_base::plugin_request(const std::string& plugin_name, const std::string& plugin_cmd,
                                   const std::vector<uint8_t>& params,
                                   std::vector<uint8_t>&       out_buffer,
                                   std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "pre bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    try {
        std::string write_error, read_error;
        auto write_ret = retry_with_sleep([&]() {
            return block_write(PLUGIN_REQUEST_WRITE_OFFSET, params, out_buffer, write_error);
        });
        if (write_ret != HAS_OK) {
            error = "plugin_request write failed: " + write_error;
            return write_ret;
        }
        std::vector<uint8_t> empty_buffer;
        auto                 read_ret = retry_with_sleep(
            [&]() {
                return block_read(PLUGIN_REQUEST_WRITE_OFFSET, PLUGIN_REQUEST_READ_LENGTH,
                                                  empty_buffer, out_buffer, read_error);
            },
            PLUGIN_REQUEST_READ_RETRY, PLUGIN_REQUEST_READ_SLEEP_MS);
        if (read_ret != HAS_OK) {
            error = "plugin_request read failed: " + read_error;
            return read_ret;
        }

        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("plugin_request error: ") + e.what();
        elog("${error}", ("error", error));
        return HAS_ERR;
    }
}

std::shared_ptr<chip_base> chip_base::get_left_chip() const {
    return m_left_chip;
}

std::shared_ptr<bus_base> chip_base::get_left_bus() const {
    return m_left_bus;
}

std::shared_ptr<bus_base> chip_base::get_host_bus() const {
    return m_host_bus;
}